#include<stdio.h>
#include"arm.h"
#include"sym.h"
#ifndef __GNUC__
#pragma warning(disable : 4700)
#endif

const char * addr_mode_sym[]=
{
"da",
"ia",
"db",
"ib",
"ed",
"ea",
"fd",
"fa"

};

const char * shift_op_sym[]=
{
"lsl",
"lsr",
"asr",
"ror",
"rrx"
};
const char * reg_sym[]=
{
"r0",
"r1",
"r2",
"r3",
"r4",
"r5",
"r6",
"r7",
"r8",
"r9",
"r10",
"r11",
"r12",
"r13",
"r14",
"pc"
};

const char *SYM_THUMB_AND="and";
const char *SYM_THUMB_EOR="eor";
const char *SYM_THUMB_LSL="lsl";
const char *SYM_THUMB_LSR="lsr";
const char *SYM_THUMB_ASR="asr";
const char *SYM_THUMB_ADC="adc";
const char *SYM_THUMB_SBC="sbc";
const char *SYM_THUMB_ROR="ror";
const char *SYM_THUMB_TST="tst";
const char *SYM_THUMB_NEG="neg";
const char *SYM_THUMB_CMP="cmp";
const char *SYM_THUMB_CMN="cmn";
const char *SYM_THUMB_ORR="orr";
const char *SYM_THUMB_MUL="mul";
const char *SYM_THUMB_BIC="bic";
const char *SYM_THUMB_MVN="mvn";
const char *SYM_THUMB_SUB="sub";
const char *SYM_THUMB_ADD="add";
const char *SYM_THUMB_BX="bx";
const char *SYM_THUMB_B="b";
const char *SYM_THUMB_BL="bl";
const char *SYM_THUMB_BEQ="beq";
const char *SYM_THUMB_BNE="bne";
const char *SYM_THUMB_BCS="bcs";
const char *SYM_THUMB_BCC="bcc";
const char *SYM_THUMB_BMI="bmi";
const char *SYM_THUMB_BPL="bpl";
const char *SYM_THUMB_BVS="bvs";
const char *SYM_THUMB_BVC="bvc";
const char *SYM_THUMB_BHI="bhi";
const char *SYM_THUMB_BLS="bls";
const char *SYM_THUMB_BGE="bge";
const char *SYM_THUMB_BLT="blt";
const char *SYM_THUMB_BGT="bgt";
const char *SYM_THUMB_BLE="ble";
const char *SYM_THUMB_LDMIA="ldmia";
const char *SYM_THUMB_LDR="ldr";
const char *SYM_THUMB_LDRB="ldrb";
const char *SYM_THUMB_LDRH="ldrh";
const char *SYM_THUMB_LDSB="ldsb";
const char *SYM_THUMB_LDSH="ldsh";
const char *SYM_THUMB_MOV="mov";
const char *SYM_THUMB_POP="pop";
const char *SYM_THUMB_PUSH="push";
const char *SYM_THUMB_STMIA="stmia";
const char *SYM_THUMB_STR="str";
const char *SYM_THUMB_STRB="strb";
const char *SYM_THUMB_STRH="strh";
const char *SYM_THUMB_SWI="swi";
const char *SYM_ARM_AND="and";
const char *SYM_ARM_EOR="eor";
const char *SYM_ARM_SUB="sub";
const char *SYM_ARM_RSB="rsb";
const char *SYM_ARM_ADD="add";
const char *SYM_ARM_ADC="adc";
const char *SYM_ARM_SBC="sbc";
const char *SYM_ARM_RSC="rsc";
const char *SYM_ARM_TST="tst";
const char *SYM_ARM_TEQ="teq";
const char *SYM_ARM_CMP="cmp";
const char *SYM_ARM_CMN="cmn";
const char *SYM_ARM_ORR="orr";
const char *SYM_ARM_MOV="mov";
const char *SYM_ARM_BIC="bic";
const char *SYM_ARM_MVN="mvn";
const char *SYM_ARM_B="b";
const char *SYM_ARM_BL="bl";
const char *SYM_ARM_BX="bx";
const char *SYM_ARM_CDP="cdp";
const char *SYM_ARM_OR="or";
const char *SYM_ARM_LDC="ldc";
const char *SYM_ARM_LDM="ldm";
const char *SYM_ARM_LDR="ldr";
const char *SYM_ARM_LDRB="ldrb";
const char *SYM_ARM_LDRSB="ldrsb";
const char *SYM_ARM_LDRSH="ldrsh";
const char *SYM_ARM_LDRUH="ldruh";
const char *SYM_ARM_LDRH="ldrh";
const char *SYM_ARM_LDRD="ldrd";
const char *SYM_ARM_MCR="mcr";
const char *SYM_ARM_MLA="mla";
const char *SYM_ARM_MRC="mrc";
const char *SYM_ARM_MRS="mrs";
const char *SYM_ARM_MSR="msr";
const char *SYM_ARM_PSR="psr";
const char *SYM_ARM_MUL="mul";
const char *SYM_ARM_STC="stc";
const char *SYM_ARM_STM="stm";
const char *SYM_ARM_STR="str";
const char *SYM_ARM_STRB="strb";
const char *SYM_ARM_STRD="strd";
const char *SYM_ARM_STRH="strh";
const char *SYM_ARM_SWI="swi";
const char *SYM_ARM_SWP="swp";
const char *SYM_ARM_BLX="blx";
const char *SYM_ARM_CLZ="clz";
const char *SYM_ARM_UD="ud";
const char *SYM_ARM_UMULL="umull";
const char *SYM_ARM_UMALL="umall";
const char *SYM_ARM_SMULL="smull";
const char *SYM_ARM_SMALL="small";

int disasm_thumb(char * buffer,THUMB_INSN_INFO insn_info,int pc)
{
	const char *insn_sym;

	switch(insn_info.code>>12)
	{
		
	case 0:
	// format 1:LSL/ LSR/ASR
		insn_sym=(insn_info.code&(1<<11))?SYM_THUMB_LSR:SYM_THUMB_LSL;
		__asm int 3;
		break;
	case 1:
		if((insn_info.code&(1<<11))==0)
		{
			insn_sym=SYM_THUMB_ASR;
		
			__asm int 3;
		}
		else
		{
	// format 2:
			insn_sym=insn_info.insn_fmt2.op?SYM_THUMB_SUB:SYM_THUMB_ADD;
					
			__asm int 3;
		}
		break;
	// format 3:
	case 2:// MOV/ CMP/ADD/SUB
		insn_sym=(insn_info.code&(1<<11))?SYM_THUMB_MOV:SYM_THUMB_CMP;
		__asm int 3;
		break;
	case 3:
		insn_sym=(insn_info.code&(1<<11))?SYM_THUMB_SUB:SYM_THUMB_ADD;
		__asm int 3;
		break;
	case 4:
	     if((insn_info.code&(1<<11)))
		 {
	//Format 6: PC-relative load
			 insn_sym=SYM_THUMB_LDR;
			 __asm int 3;
		 }
		 else
	// Format 4: ALU operations
		if((insn_info.code&(1<<10))==0)
		{
			static const char * thumb_fmt4[]={
					SYM_THUMB_AND, 
					SYM_THUMB_EOR,
					SYM_THUMB_LSL, 
					SYM_THUMB_LSR, 
					SYM_THUMB_ASR, 
					SYM_THUMB_ADC, 
					SYM_THUMB_SBC, 
					SYM_THUMB_ROR, 
					SYM_THUMB_TST, 
					SYM_THUMB_NEG,
					SYM_THUMB_CMP, 
					SYM_THUMB_CMN, 
 					SYM_THUMB_ORR, 
					SYM_THUMB_MUL, 
					SYM_THUMB_BIC,
					SYM_THUMB_MVN
				};
			insn_sym=thumb_fmt4[insn_info.insn_fmt4.op];
			__asm int 3;
		}

		else
		{
	//Format 5: Hi register operations/branch exchange
			static const char * thumb_fmt5[]={
				SYM_THUMB_ADD,SYM_THUMB_CMP,SYM_THUMB_MOV,SYM_THUMB_BX
			};
			insn_sym=thumb_fmt5[insn_info.insn_fmt5.op];
			__asm int 3;
		}
		break;
	case 5:
		if((insn_info.code&(1<<9))==0)
		{
	//Format 7: load/store with register offset
			static const char * thumb_fmt7[]={
				SYM_THUMB_STR,SYM_THUMB_STRB,SYM_THUMB_LDR,SYM_THUMB_LDRB
			};
			// {L,B}
			insn_sym=thumb_fmt7[(insn_info.code>>10)&3];
			__asm int 3;
		}
		else
		{
	//Format 8: load/store sign-extended byte/halfword
			static const char * thumb_fmt8[]={
				SYM_THUMB_STRH,SYM_THUMB_LDRH,SYM_THUMB_LDSB,SYM_THUMB_LDSH
			};
			// {H,S}
			insn_sym=thumb_fmt8[(insn_info.code>>10)&3];
			__asm int 3;
		}
		break;
	case 6:
	case 7:
		{
	//Format 9: load/store with immediate offset
			static const char * thumb_fmt9[]={
				SYM_THUMB_STR,SYM_THUMB_LDR,SYM_THUMB_LDRB,SYM_THUMB_STRB
			};
			// {B,L}
			insn_sym=thumb_fmt9[(insn_info.code>>11)&3];
			__asm int 3;
		}

		break;

	case 8:
	//Format 10: load/store halfword
		insn_sym=(insn_info.code&(1<<11))?SYM_THUMB_STRH:SYM_THUMB_LDRH;
		__asm int 3;
		break;

	case 9:
	//Format 11: SP-relative load/store
		insn_sym=(insn_info.code&(1<<11))?SYM_THUMB_STR:SYM_THUMB_LDR;
		__asm int 3;
		break;
	case 10:
	//Format 12: load address
		insn_sym=SYM_THUMB_ADD;
		__asm int 3;
		break;

	case 11:
		if((insn_info.code&(1<<9))==0)
		{
	//Format 13: add offset to Stack Pointer
		insn_sym=SYM_THUMB_ADD;
		__asm int 3;
		}
		else
		{
	//Format 14: push/pop registers
			insn_sym=(insn_info.code&(1<<11))?SYM_THUMB_PUSH:SYM_THUMB_POP;
			__asm int 3;
		}
		break;

	case 12:
	//Format 15: multiple load/store
		insn_sym=(insn_info.code&(1<<11))?SYM_THUMB_STMIA:SYM_THUMB_LDMIA;
		__asm int 3;
		break;
	case 13:
	//Format 16: conditional branch
		if(insn_info.insn_fmt16.cond<0xe)
		{
			static const char * thumb_fmt16[]={
				SYM_THUMB_BEQ, 
				SYM_THUMB_BNE, 
				SYM_THUMB_BCS, 
				SYM_THUMB_BCC, 
				SYM_THUMB_BMI, 
				SYM_THUMB_BPL, 
				SYM_THUMB_BVS, 
				SYM_THUMB_BVC, 
				SYM_THUMB_BHI, 
				SYM_THUMB_BLS,
				SYM_THUMB_BGE, 
				SYM_THUMB_BLT, 
				SYM_THUMB_BGT, 
				SYM_THUMB_BLE
				};
			insn_sym=thumb_fmt16[insn_info.insn_fmt16.cond];
			__asm int 3;
		}
		else if(insn_info.insn_fmt16.cond==0xf)
		{
	//Format 17: software interrupt
			insn_sym=SYM_THUMB_SWI;
			__asm int 3;
		}
		else
			BUG(UD,__LINE__,__FILE__);
		break;
	case 14:
	//Format 18: unconditional branch
		insn_sym=SYM_THUMB_B;
		__asm int 3;
		break;
	case 15:
	//Format 19: long branch with link
		insn_sym=SYM_THUMB_BL;
		__asm int 3;
		break;

	}


	return 0;
}

static const char * cond_sym[]=
{
"eq",
"ne",
"cs",
"cc",
"mi",
"pl",
"vs",
"vc",
"hi",
"ls",
"ge",
"lt",
"gt",
"le",
"",//al
""
};

int disasm_arm(char * buffer,ARM_INSN_INFO insn_info,int pc)
{
	const char *insn_sym;
	int len=0;


	// extensions:
	if((insn_info.code&0xfd70f000)==0xf550f000)
	{
		//pld xx
		len=sprintf(buffer,"pld");
		goto out;
	}
	switch((insn_info.code>>24)&0xf)
	{
	case 0:

		if((insn_info.code&0xf0)==0x90)
		{
			if((insn_info.code&(1<<23))==0)
			{
		//fmt 2:Multiply and Multiply-Accumulate (MUL, MLA)
			// 22,23==0
				if((insn_info.code&(1<<22))!=0)
						BUG(UD,__LINE__,__FILE__);

				insn_sym=((insn_info.code&(1<<21))==0)?SYM_ARM_MUL:SYM_ARM_MLA;
//MUL{cond}{S} Rd,Rm,Rs
//MLA{cond}{S} Rd,Rm,Rs,Rn
				len=sprintf(buffer,"%s%s",
					insn_sym,
					cond_sym[insn_info.insn_fmt3.cond]);

				if(insn_info.insn_fmt3.S)buffer[len++]='s';

				while(len<10) buffer[len++]=' ';
				len+=sprintf(buffer+len,"%s,%s,%s",
						reg_sym[insn_info.insn_fmt2.Rd],
						reg_sym[insn_info.insn_fmt2.Rm],
						reg_sym[insn_info.insn_fmt2.Rs]
					);


				if((insn_info.code&(1<<21))!=0)
				{
					len+=sprintf(buffer+len,",%s",
						reg_sym[insn_info.insn_fmt2.Rn]);
				}

				break;
			}
			else
			{
		//fmt 3:Multiply Long and Multiply-Accumulate Long (MULL,MLAL)
		// bit 23==1
				//RdLo,RdHi,Rm,Rs
				
				static const char * arm_fmt3[]={
					SYM_ARM_UMULL,
					SYM_ARM_UMALL,
					SYM_ARM_SMULL,
					SYM_ARM_SMALL,
				};
				insn_sym=arm_fmt3[((insn_info.code>>21)&3)];
				len=sprintf(buffer,"%s%s",
					insn_sym,
					cond_sym[insn_info.insn_fmt3.cond]);

				if(insn_info.insn_fmt3.S)buffer[len++]='s';

				while(len<10) buffer[len++]=' ';
				len+=sprintf(buffer+len,"%s,%s,%s,%s",
					reg_sym[insn_info.insn_fmt3.RdLo],
					reg_sym[insn_info.insn_fmt3.RdHi],
					reg_sym[insn_info.insn_fmt3.Rm],
					reg_sym[insn_info.insn_fmt3.Rn]
					);
				break;

			}

		}
		

		goto fall1;
	case 1:


		//fmt 5:Branch and Exchange (BX)
		//BX{cond} Rn
		if((insn_info.code&0x0ffffff0)==0x012fff10)
		{
				insn_sym=SYM_ARM_BX;
				len=sprintf(buffer,"%s%s",
					insn_sym,
					cond_sym[insn_info.insn_fmt5.cond]);
				while(len<10) buffer[len++]=' ';
				len+=sprintf(buffer+len,"%s",reg_sym[insn_info.insn_fmt5.Rm]);
				break;
		}

		//fmt 4:Single Data Swap (SWP)
		if((insn_info.code&0x00b00f90)==0x90)
		{
			//SWP{<cond>} <Rd>, <Rm>, [<Rn>]
				insn_sym=SYM_ARM_SWP;
				len=sprintf(buffer,"%s%s",
				insn_sym,
				cond_sym[insn_info.swp.cond]);
				while(len<10) buffer[len++]=' ';
				
				len+=sprintf(buffer+len,
					"%s,%s,[%s]",
					reg_sym[insn_info.swp.Rd],
					reg_sym[insn_info.swp.Rm],
					reg_sym[insn_info.swp.Rn]);
				break;
		}

		//CLZ{<cond>} <Rd>, <Rm>
		if((insn_info.code&0xfff0ff0)==0x16f0f10)
		{
				insn_sym=SYM_ARM_CLZ;

				len=sprintf(buffer,"%s%s",
				insn_sym,
				cond_sym[insn_info.clz.cond]);

				while(len<10) buffer[len++]=' ';
				
				len+=sprintf(buffer+len,
					"%s,%s",
					reg_sym[insn_info.clz.Rd],
					reg_sym[insn_info.clz.Rm]);
				break;
		}
		//fmt 6,7:Halfword and Signed Data Transfer
fall1:
		if((insn_info.code&0x00000090)==0x90)
		{
			__u8 lsh;

			lsh=((insn_info.code&0x60)>>5)|((insn_info.code>>18)&4);


			//
			// bit L S H
			//
			switch(lsh)
			{
			case 4:
				// single data ,won't reach
				__asm int 3;
				break;
				//L=1, S=0, H=1 Load unsigned halfword.
			case 5:
				insn_sym=SYM_ARM_LDRUH;
				break;
				//L=1, S=1, H=0 Load signed byte.
			case 6:
					insn_sym=SYM_ARM_LDRSB;
				break;
				//L=1, S=1, H=1 Load signed halfword.
			case 7:
				insn_sym=SYM_ARM_LDRSH;
				break;								

			case 0:
				// single data ,won't reach
				__asm int 3;
				break;
			//L=0, S=0, H=1 Store halfword.
			case 1:
				insn_sym=SYM_ARM_STRH;
				break;
			//L=0, S=1, H=0 Load doubleword.
			case 2:
			    insn_sym=SYM_ARM_LDRD;
				break;
	
			//L=0, S=1, H=1 Store doubleword.
			case 3:
			    insn_sym=SYM_ARM_STRD;
				break;
		
			}
			len=sprintf(buffer,
				"%s%s",
				insn_sym,
				cond_sym[insn_info.insn_fmt6.cond]);
		//
		//Rd,<address>
		//

			while(len<10) buffer[len++]= ' ';

			len+=sprintf(buffer+len,"%s,",reg_sym[insn_info.insn_fmt6.Rd]);

	

			if((insn_info.code&(1<<22))!=0)
			{
				__u32 offset;

				offset=insn_info.insn_fmt6a.immL|	(insn_info.insn_fmt6a.immH<<4);
				// offset is not 0
				if(offset)
					len+=sprintf(buffer+len,
						!insn_info.insn_fmt6a.P?"[%s],#%c0x%x":"[%s,#%c0x%x]",
						reg_sym[insn_info.insn_fmt6a.Rn],
						insn_info.insn_fmt6a.U?' ':'-',
						offset
						);
				else
					len+=sprintf(buffer+len,"[%s]",
						reg_sym[insn_info.insn_fmt6a.Rn]);
			}
			else
			{
				// reister offset/index
				len+=sprintf(buffer+len,
						!insn_info.insn_fmt8.P?"[%s],%c%s":"[%s,%c%s]",
						reg_sym[insn_info.insn_fmt6.Rn],
						insn_info.insn_fmt6a.U?'+':'-',
						reg_sym[insn_info.insn_fmt6.Rm]
						);
	
			}
			if(insn_info.insn_fmt6a.W&&
				!insn_info.insn_fmt6a.P) 
				buffer[len++]='!';
			break;
		}

		//MRS (transfer PSR contents to a register):16-21
		if(((insn_info.code)&0xfbf0000)==0x10f0000)
		{
			len=sprintf(buffer,"%-10s%s,%cPSR",
				SYM_ARM_MRS,
				reg_sym[insn_info.insn_fmt1.Rd],
				insn_info.code&(1<<22)?'S':'C');
			break;
		}
		//MSR (transfer register contents to PSR)
		//Register operand:
#if 0
		if(((insn_info.code)&0xfbf0fff0)==0x120f000)
		{
			// 0~11==0
			if((insn_info.code&0xff0)!=0)
				BUG(UD,__LINE__,__FILE__);

			len=sprintf(buffer,"%-10s%cPSR,%s",
				SYM_ARM_MSR,
				insn_info.code&(1<<22)?'S':'C',
				reg_sym[insn_info.msr_reg.Rm]
			);
			break;
		}
#endif
	//MSR (transfer register contents to PSR)
    //      only flags
		if(((insn_info.code)&0x1b0fff0)==0x120f000)
		{


			len=sprintf(buffer,"%-10s%cPSR",
				SYM_ARM_MSR,
				insn_info.code&(1<<22)?'S':'C');

			//

			buffer[len++]='_';
			if(insn_info.code&(1<<19)) buffer[len++]='f';
			if(insn_info.code&(1<<18)) buffer[len++]='s';
			if(insn_info.code&(1<<17)) buffer[len++]='x';
			if(insn_info.code&(1<<16)) buffer[len++]='c';

			if(insn_info.code&(1<<25))
			{
				len+=sprintf(buffer+len,",#0x%x",
					insn_info.msr_imm.imm);
				
				if(insn_info.msr_imm.rotate)
					len+=sprintf(buffer+len," LSL #%d",insn_info.msr_imm.rotate*2);
		
			}
			else
			{

				len+=sprintf(buffer+len,",%s",	reg_sym[insn_info.msr_reg.Rm]);
			}

			break;
		}
		//BLX (2)
		if(((insn_info.code)&0x0ff000f0)==0x1200030)
		{
			if((insn_info.code&0xfff00)!=0)
				BUG(UD,__LINE__,__FILE__);

			len=sprintf(buffer,"%s%s",
					SYM_ARM_BLX,
					cond_sym[insn_info.blx2.cond]);
			while(len<10) buffer[len++]=' ';

			len+=sprintf(buffer+len,"%s",
				reg_sym[insn_info.blx2.Rm]);

			break;
		}

		// fall through
		goto fall0;
	case 3:
		// msr 
		if((insn_info.code&0x0fb0f000)==0x0320f000)
		{
			len=sprintf(buffer,"%-10s%cPSR",
				SYM_ARM_MSR,
				insn_info.code&(1<<22)?'S':'C');

			//
			if(insn_info.msr_imm.field)
			{
				buffer[len++]='_';
				if(insn_info.code&(1<<16)) buffer[len++]='c';
				if(insn_info.code&(1<<17)) buffer[len++]='x';
				if(insn_info.code&(1<<18)) buffer[len++]='s';
				if(insn_info.code&(1<<19)) buffer[len++]='f';
			}
			len+=sprintf(buffer+len,",# 0x%x LSR %d;0x%x",
				insn_info.msr_imm.imm,insn_info.msr_imm.rotate*2,
				insn_info.msr_imm.imm>>(insn_info.msr_imm.rotate*2)
			);
			break;
		}
	case 2:	


fall0:
		{
		// fmt 1:Data Processing

				static const char * arm_fmt1[]={
				SYM_ARM_AND, 
				SYM_ARM_EOR, 	
				SYM_ARM_SUB, 
				SYM_ARM_RSB, 
				SYM_ARM_ADD, 
				SYM_ARM_ADC, 
				SYM_ARM_SBC,
				SYM_ARM_RSC, 
				SYM_ARM_TST, 
				SYM_ARM_TEQ, 
				SYM_ARM_CMP,
				SYM_ARM_CMN, 
				SYM_ARM_ORR, 
				SYM_ARM_MOV, 
				SYM_ARM_BIC, 
				SYM_ARM_MVN
				};
			insn_sym=arm_fmt1[insn_info.insn_fmt1.Opcode];
			switch(insn_info.insn_fmt1.Opcode+ARM_AND)
			{
		//1 MOV,MVN (single operand instructions.)
			case ARM_MOV:
			case ARM_MVN:
		//<opcode>{cond}{S} Rd,<Op2>
				len=sprintf(buffer,
					"%s%s",
					insn_sym,
					cond_sym[insn_info.insn_fmt1.cond]);
				if(insn_info.insn_fmt1.S) buffer[len++]='s';
				while(len<10) buffer[len++]=' ';
                len+=sprintf(buffer+len,
					"%s,",
					reg_sym[insn_info.insn_fmt1.Rd]);

				break;

		//2 CMP,CMN,TEQ,TST (instructions which do not produce a result.)
		//<opcode>{cond} Rn,<Op2>
			case ARM_CMP:
			case ARM_CMN:
			case ARM_TEQ:
			case ARM_TST:
				len=sprintf(buffer,"%s%s",
					insn_sym,
					cond_sym[insn_info.insn_fmt1.cond]);
				while(len<10) buffer[len++]=' ';
                len+=sprintf(buffer+len,
					"%s,",
					reg_sym[insn_info.insn_fmt1.Rn]);

				if(insn_info.insn_fmt1.S==0)
					BUG(UD,__LINE__,__FILE__);
				break;
		//3 AND,EOR,SUB,RSB,ADD,ADC,SBC,RSC,ORR,BIC
		//<opcode>{cond}{S} Rd,Rn,<Op2>
			default:
				len=sprintf(buffer,"%s%s",
					insn_sym,
					cond_sym[insn_info.insn_fmt1.cond]);
				if(insn_info.insn_fmt1.S) buffer[len++]='s';
				while(len<10) buffer[len++]=' ';
                len+=sprintf(buffer+len,
					"%s,%s,",
					reg_sym[insn_info.insn_fmt1.Rd],
					reg_sym[insn_info.insn_fmt1.Rn]);
				break;
			}
operand2:
		//
		//  <operand2>
		//
			if(insn_info.insn_fmt1.I)
			{

				__u32 operand2=insn_info.insn_fmt1.operand2;
				__u32 rotate_cnt=2*insn_info.insn_fmt1.rotate_imm;
				__u32 mask;
				mask=(1<<rotate_cnt)-1;	

				__u32  shifter_operand;
				shifter_operand=(operand2&mask)<<(32-rotate_cnt);
				shifter_operand|=(operand2&~mask)>>(rotate_cnt);
				len+=sprintf(buffer+len,
					"# 0x%x",
					shifter_operand);
			}
			else
			{
				len+=sprintf(buffer+len,"%s",reg_sym[insn_info.insn_fmt1a.Rm]);
				if(!insn_info.insn_fmt1a.sI)
				{
					if(insn_info.insn_fmt1a.shift_imm)
					len+=sprintf(buffer+len,
						",%s  #%d",
						shift_op_sym[insn_info.insn_fmt1a.sOp],
						insn_info.insn_fmt1a.shift_imm);
				}
				else //
				{
					len+=sprintf(buffer+len,
					",%s  %s",
					shift_op_sym[insn_info.insn_fmt1b.sOp],
					reg_sym[insn_info.insn_fmt1b.Rs]);
				}
			}

		}
		break;

	case 6:
	case 7:
#if 0
		if((insn_info.code&0x10)==0)
		{
		//fmt 9:Undefined Instruction
		//undefined instruction trap will be taken
		insn_sym=SYM_ARM_UD;
		__asm int 3;
		break;
		}
#endif
	case 4:
	case 5:
		// fmt 8:Single Data Transfer (LDR, STR)
		//<LDR|STR>{cond}{B}{T} Rd,<Address>
		insn_sym=((insn_info.code&(1<<20))!=0)?SYM_ARM_LDR:SYM_ARM_STR;
		len=sprintf(buffer,"%s%s",
			insn_sym,
			cond_sym[insn_info.insn_fmt8.cond]);
		if(insn_info.insn_fmt8.B) buffer[len++]='b';
		if(insn_info.insn_fmt8.W&&!insn_info.insn_fmt8.P)
			buffer[len++]='t';


        while(len<10) buffer[len++]= ' ';

		len+=sprintf(buffer+len,"%s,",reg_sym[insn_info.insn_fmt8.Rd]);

			
		if((insn_info.code&(1<<25))==0)
		{
			// offset is not 0
			if(insn_info.insn_fmt8.Offl)
				len+=sprintf(buffer+len,
					!insn_info.insn_fmt8.P?"[%s],%c #0x%x":"[%s,%c #0x%x]",
					reg_sym[insn_info.insn_fmt8.Rn],
					insn_info.insn_fmt8.U?' ':'-',
			        insn_info.insn_fmt8.Offl
					);
			else
				len+=sprintf(buffer+len,"[%s]",
					reg_sym[insn_info.insn_fmt8.Rn]);
		}
		else
		{
			if(!insn_info.insn_fmt8a.sI)
			{
				//
				//FIXME, if RRX, no imm
				//
				if(insn_info.insn_fmt8a.shift_imm)
				len+=sprintf(buffer+len,
					!insn_info.insn_fmt8.P?"[%s],%c %s,%s 0x%x":"[%s,%c %s,%s 0x%x]",
					reg_sym[insn_info.insn_fmt8a.Rn],
					insn_info.insn_fmt8.U?' ':'-',
					reg_sym[insn_info.insn_fmt8a.Rm],
					shift_op_sym[insn_info.insn_fmt8a.sOp],
					insn_info.insn_fmt1a.shift_imm);
				else
				len+=sprintf(buffer+len,
					!insn_info.insn_fmt8.P?"[%s],%c %s":"[%s,%c %s]",
					reg_sym[insn_info.insn_fmt8a.Rm],
					insn_info.insn_fmt8.U?' ':'-',
					reg_sym[insn_info.insn_fmt8a.Rm]);
			}
			else //H|S
			{
				len+=sprintf(buffer+len,
				",%s  %s",
				shift_op_sym[insn_info.insn_fmt8b.sOp],
				reg_sym[insn_info.insn_fmt8b.Rs]);
			}

		}
		//
		// write back for prefix
		//
		if(insn_info.insn_fmt8.P)
			if(insn_info.insn_fmt8.W)buffer[len++]='!';

		break;

	case 8:
	case 9:
		// fmt 10:Block Data Transfer (LDM, STM)
		//LDM|STM{<cond>}<addressing_mode> <Rn>{!}, <registers>{^}
		insn_sym=((insn_info.code&(1<<20)))?SYM_ARM_LDM:SYM_ARM_STM;
		{
			__u8 addr_mode=insn_info.insn_fmt10a.addr_mode;
			if(insn_info.insn_fmt10.Rn==R13)
			{
				//if(insn_info.insn_fmt10.L)
				//	addr_mode^=3;

				//addr_mode+=4;
			}

			len=sprintf(buffer,"%s%s%s",
				insn_sym,
				cond_sym[insn_info.insn_fmt10.cond],
				addr_mode_sym[addr_mode]);

			while(len<10) buffer[len++]= ' ';

			len+=sprintf(buffer+len,"%s",reg_sym[insn_info.insn_fmt10.Rn]);
			if(insn_info.insn_fmt10.W)
				buffer[len++]='!';

			//
			// register list
			//
			buffer[len++]=',';
			buffer[len++]='{';
			int i,j;

			i=0;
			for(;;)
			{
				while(0==(insn_info.insn_fmt10.reg_list&(1<<i))&&i<16)i++;

				if(i==16) break;
				j=i;
				while (insn_info.insn_fmt10.reg_list&(1<<i)&&i<16) i++;

				if(i-j>1)
				len+=sprintf(buffer+len,"r%d-r%d,",j,i-1);
				else
				len+=sprintf(buffer+len,"r%d,",j);
				
			}
		

			buffer[len-1]='}';
			if(/*insn_info.insn_fmt10.W
			   &&insn_info.insn_fmt10.reg_list&(1<<15)*/(insn_info.code&(5<<20))==(5<<20))//LDM(2),LDM(3)
				buffer[len++]='^';
		}
		break;
	case 10:
	case 11:
		{
			__u32 target;
			__u32 offset;
			char * sym;

			//
			// unconditional extension
			//
			if(((insn_info.code>>28)&0xf)==0xf)
			{
			//1. Sign-extending the 24-bit signed (two's complement) immediate to 30 bits
			//2. Shifting the result left two bits to form a 32-bit value
			//3. Setting bit[1] of the result of step 2 to the H bit
			//4. Adding the result of step 3 to the contents
				insn_sym=SYM_ARM_BLX;
				offset=((insn_info.insn_fmt11a.offset<<2)|((insn_info.code>>23)&2));
			}
			else

			{


			// fmt 11:Branch and Branch with Link (B, BL)
			//Branch instructions contain a signed 2's complement 24 bit offset. 
			//This is shifted left two bits, sign extended to 32 bits, and added to the PC.
			//the PC to be 2 words (8 bytes) ahead of the current instruction.
				insn_sym=((insn_info.code&(1<<24))==0)?SYM_ARM_B:SYM_ARM_BL;
				offset=(insn_info.insn_fmt11a.offset<<2);
			}
			target=offset+pc+8;
			len=sprintf(buffer,"%s%s", 
				insn_sym,
				cond_sym[insn_info.insn_fmt11.cond]);
			while(len<10) buffer[len++]=' ';
			len+=sprintf(buffer+len,"%08x",target);
			
			sym=sym_lookup(target);
			if(sym) len+=sprintf(buffer+len,"(%s)",sym);

		}
		break;
	case 12:
	case 13:
		// fmt 12:Coprocessor Data Transfers (LDC, STC)
		insn_sym=((insn_info.code&(1<<20))==0)?SYM_ARM_STC:SYM_ARM_LDC;
		break;
	case  14:
		if((insn_info.code&0x10)==0)
		{
		// fmt 13:Coprocessor Data Operations (CDP)
			insn_sym=SYM_ARM_CDP;
			__asm int 3;
		}
		else
		{
		// fmt 14:Coprocessor Register Transfers (MRC, MCR)
			insn_sym=(insn_info.insn_fmt14.L)?SYM_ARM_MRC:SYM_ARM_MCR;
			//__asm int 3;
			//MCR{<cond>} p15, 0, <Rd>, <CRn>, <CRm>{, <opcode2>}
			len=sprintf(buffer,"%s%s", 
				insn_sym,
				cond_sym[insn_info.insn_fmt15.cond]);

			while(len<10) buffer[len++]=' ';

			len+=sprintf(buffer+len,"p%d, %d, r%d, c%d, c%d, {0x%x}",
				insn_info.insn_fmt14.CP,
				insn_info.insn_fmt14.CPOP,
				insn_info.insn_fmt14.Rd,
				insn_info.insn_fmt14.CRn,
				insn_info.insn_fmt14.CRm,
				insn_info.insn_fmt14.opCode2
				);
		}
		break;
	case 15:
	   //Format 15:Software Interrupt (SWI)
		//SWI{<cond>} <immed_24>
		insn_sym=SYM_ARM_SWI;
			len=sprintf(buffer,"%s%s", 
				insn_sym,
				cond_sym[insn_info.insn_fmt15.cond]);
		while(len<10) buffer[len++]=' ';
		len+=sprintf(buffer+len,"# 0x%x",insn_info.insn_fmt15.immed_24);
		break;

	}

out:
	return len;
}

